package com.java.util;


import com.java.util.pojo.IntrospectionUser;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
 * Java bean的内省机制测试
 * 对比反射：
 * @see MapToBeanReflection
 */
public class IntrospectionTest {


    public static void main(String[] args) {
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("name", "张三");
        userMap.put("age", 25);
        userMap.put("email", "zhangsan@example.com");

        // 将Map转换为User对象
        IntrospectionUser user = toBean(IntrospectionUser.class, userMap);
        System.out.println(user);

        // 输出结果
        /*System.out.println(user.getName());  // 输出: 张三
        System.out.println(user.getAge());   // 输出: 25
        System.out.println(user.getEmail()); // 输出: zhangsan@example.com*/
    }


    public static <T> T toBean(Class<T> clazz, Map map) {
        // 声明一个泛型对象，初始化为null
        T obj = null;
        try {
            // 获取目标类的BeanInfo信息，用于内省(Introspection)
            BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
            // 通过反射创建目标类的实例
            // clazz.newInstance()已过时，建议使用clazz.getDeclaredConstructor().newInstance()
            obj = clazz.newInstance();
            // 获取目标类的所有属性描述符
            // 如果目标类没有getter属性描述符数组将为空
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

            // 遍历所有属性描述符
            for (int i = 0; i < propertyDescriptors.length; i++) {
                PropertyDescriptor descriptor = propertyDescriptors[i];
                // 获取属性名称
                String propertyName = descriptor.getName();

                // 检查Map中是否包含该属性名对应的键
                if (map.containsKey(propertyName)) {
                    // 从Map中获取对应的值
                    Object value = map.get(propertyName);

                    // 如果值为null或空字符串，则将其设为null
                    if (null == value || "".equals(value)) {
                        value = null;
                    }

                    // 创建参数数组
                    Object[] args = new Object[1];
                    // 调用方法设置属性值（此处的invoke方法需要查看其具体实现）
                    invoke(obj, descriptor, value, args);
                }
            }
        } catch (Exception e) {
            // 记录错误日志，包含异常信息和Map内容
//            System.err.println("MapHelpers1 toBean,e= " + e + " map= " + Optional.ofNullable(map).map(Object::toString).orElse(""));
            e.printStackTrace();
        }
        // 返回填充后的对象
        return obj;
    }


    private static <T> void invoke(T obj, PropertyDescriptor descriptor, Object value, Object[] args) throws InvocationTargetException, IllegalAccessException {
        // 将值转换为属性的类型
        args[0] = convertValType(value, descriptor.getPropertyType());
        // 调用属性的写方法（setter）来设置属性值
        // 如果目标类没有setter方法将会抛出异常
        descriptor.getWriteMethod().invoke(obj, args);
    }

    private static Object convertValType(Object value, Class<?> fieldTypeClass) {
        Object retVal;

        if (fieldTypeClass.isAssignableFrom(Long.class)
                || fieldTypeClass.isAssignableFrom(long.class)) {
            retVal = Long.parseLong(value.toString());
        } else if (fieldTypeClass.isAssignableFrom(Integer.class)
                || fieldTypeClass.isAssignableFrom(int.class)) {
            retVal = Integer.parseInt(value.toString());
        } else if (fieldTypeClass.isAssignableFrom(Float.class)
                || fieldTypeClass.isAssignableFrom(float.class)) {
            retVal = Float.parseFloat(value.toString());
        } else if (fieldTypeClass.isAssignableFrom(Double.class)
                || fieldTypeClass.isAssignableFrom(double.class)) {
            retVal = Double.parseDouble(value.toString());
        } else if (fieldTypeClass.isAssignableFrom(Boolean.class)
                || fieldTypeClass.isAssignableFrom(boolean.class)) {
            retVal = Boolean.valueOf(value.toString());
        } else {
            retVal = value;
        }
        return retVal;
    }

}
